package state;

import java.util.Arrays;

import movgen.BitUtil;
import movgen.EnPassant;
import movgen.Move2;
import movgen.Movegen;
import movgen.Util;

import com.alonsoruibal.chess.bitboard.BishopMagic;
import com.alonsoruibal.chess.bitboard.RookMagic;

//tropism : distance = Max( abs(Rank(king) - rank(square)), abs(file(king) - file(square))); 
//file = index & 7; 
//rank = index >> 3;

public class GameState {

	public static final int QUEEN_COUNT_BONUS = 20;
	public static final int KING_WALK_PENALTY=-30;
	public static final int WING_PAWN_PUSH_PENALTY = -25;
	public static final int ROOK_COUNT_BONUS = 20;
	private static final int CASTLING_BONUS = 30;
	private static final int ROOK_ON_SEVENTH_BONUS = 20;
	private static final int WING_PAWN_PUSH_BONUS = 20;
	private static final int TWO_BISHOPS_BONUS = 15;
	
	//private static final int ISOLATED_PAWN_PENALTY = -20;
	private static final int PAWN_REMOVAL_BONUS = 7;
	//private static final int ROOK_OPEN_OR_HALF_OPEN_FILE_BONUS = 15;
	
	private static final int ENDGAME_MATERIAL_VALUE = 3000;
	
	public static final int QUEEN_VALUE = 985;  // slightly higher since they don't use pos. tables
	private static final int ROOK_VALUE = 515;  // slightly higher since they don't use pos. tables
	private static final int BISHOP_VALUE = 350;
	private static final int KNIGHT_VALUE = 325;
	public static final int PAWN_VALUE = 100;
	
	public static final int WHITE=0;
	public static final int BLACK=1;
	
	public static final int[] valueMap = { PAWN_VALUE, PAWN_VALUE, KNIGHT_VALUE,  KNIGHT_VALUE, 
		BISHOP_VALUE, 	BISHOP_VALUE,   ROOK_VALUE, ROOK_VALUE,  QUEEN_VALUE , QUEEN_VALUE};
	
	public static final int WP=0;
	public static final int BP=1;
	public static final int WN=2;
	public static final int BN=3;
	public static final int WB=4;
	public static final int BB=5;
	public static final int WR=6;
	public static final int BR=7;
	public static final int WQ=8;
	public static final int BQ=9;
	public static final int WK=10;
	public static final int BK=11;
	
	public static final  int WHITEPIECES=12;
	public static final  int BLACKPIECES=13;
	public static final  int ALLPIECES=14;
	
	//used for Castling Privileges
	public static final int WKSIDE=0;
	public static final int WQSIDE=1;
	public static final int BKSIDE=2;
	public static final int BQSIDE=3;
	
	public static final int NUMBITBOARDS = 15;
	public static final int MAX_PLIES=200;
	
	//TROPISM
	private static int[][] distance= new int[64][64];
	
	public static int[] moveHistory   = new int[MAX_PLIES];
	public static int[] capturedPieces= new int [MAX_PLIES];
	public static int   moveCounter=0;
	
	public static final int EMPTY = -1;
	
	public int[] board= new int[64];
	
	public long[] 			bitboard= new long[NUMBITBOARDS];
	public int              color;  //player to move
	public int 				flags;
	public long 			hash;
	public int[] 			material =   new int[2];
	public int []			positional = new int[2];

	//used for extensions
	public boolean	        promotion=false;
	public boolean seventhRankExtension=false;
	
	private static final int[] castleRook={WR,BR};
	
	public static final int SQUARE_MASK = 63; // 6 bits, masks out the rest of the int when it has been shifted so we only get the information we need
	public static final int PIECE_MASK  = 15; // 4 bits
	public static final int TYPE_MASK = 31; // 5 bits
	public static final int ORDERING_CLEAR = 0x1FFFFFF; // = 00000001111111111111111111111111 use with & which clears the ordering value
	public static final int EP_SHIFT = 25; 
    public static final int EP_CLEAR = 0x1FFFFFF;
    
	public static final int eps = 0 << EP_SHIFT;
	
	public static final int negWks = ~ ( 1 << WKSIDE);
	public static final int negWqs = ~ ( 1 << WQSIDE);
	public static final int negBks = ~ ( 1 << BKSIDE);
	public static final int negBqs = ~ ( 1 << BQSIDE);
	
	public static final int TO_SHIFT = 6; // Number of bits to shift to get the to-index
	public static final int PIECE_SHIFT = 12; // To get the piece moving
	public static final int CAPTURE_SHIFT = 16; // To get the capture
	public static final int TYPE_SHIFT = 20; // To the move type
	public static final int ORDERING_SHIFT = 25; // To get the ordering value
	
	private static int[]kingSideFlags={negWks,negBks};
	private static int[]queenSideFlags={negWqs,negBqs};

    private static final long[] ksCastled={7L,  504403158265495552L};
    private static final long[] qsCastled={224L,2305843009213693952L};
    
    private static final long[] wreckedKSPawnShield = { 50331648L, 12884901888L   };
    private static final long[] wreckedQSPawnShield = { 3221225472L, 824633720832L   };
    private static final long[] safeMiddleGameKingSquares = {107L, 7710162562058289152L    };
    
    private static final long[] seventhRank = { Movegen.RANK7, Movegen.RANK2};
    
    private static final 	int[] rookHomeQS = { 56, 0 };
    private static final 	int[] rookHomeKS = { 63, 7};
    private static final 	int[] kingHome = { 60, 4 };
    private static final 	int[] king = { WK,BK };
    
    private static final int[] pp={WQ, BQ};
    public static final long[][] minedBitboards = { { 4L, 16L}, {288230376151711744L, 1152921504606846976L}};
	private static final long[] castlingBitboards =  {6L, 112L, 432345564227567616L, 8070450532247928832L};
	
	
	
	// state history used for repetition detection
    public static int currentPly;
	//public static long[]stateHistory=new long[MAX_PLIES];  
	
	// state history used for repetition detection
	
    static {
    	initDistanceTable();
    }
	public static PawnStructureTable pawnStructureTable= new PawnStructureTable(500000);
	//public static RookTable rookTable= new RookTable(500000);
	
	public static void resetMoveHistory() {
		//Arrays.fill(moveHistory, 0);
		moveCounter=0;
	}
	
	public boolean isSameSquareRecapture() {
		    if (moveCounter < 2)  return false;
			int lastVictim     = capturedPieces[moveCounter -1];
			int previousVictim = capturedPieces[moveCounter -2];
			if (lastVictim == -1 || previousVictim == -1 ) return false;
			int value1 = valueMap[lastVictim];
			int value2 = valueMap[previousVictim];
			if ( value1 != value2)  return false;
			int move1 = moveHistory[moveCounter -1];
			int move2 = moveHistory[moveCounter -2];
			
			int toSquare1 =  ((move1 >> TO_SHIFT) & SQUARE_MASK);	
			int toSquare2 =  ((move2 >> TO_SHIFT) & SQUARE_MASK);	
			
		    if (toSquare1==toSquare2) {
		    	return true;
		    }
		    return false;
	}

	public GameState(GameState gs) {
		System.arraycopy(gs.bitboard, 0, this.bitboard, 0, NUMBITBOARDS);
		System.arraycopy(gs.board, 0, this.board, 0, 64);
		this.flags=gs.flags;
		this.color=gs.color;
		this.hash=gs.hash;
		System.arraycopy(gs.material, 0, this.material, 0, 2);
		System.arraycopy(gs.positional, 0, this.positional, 0, 2);
	}
     
	public  int getEPSquare() {
		return  flags >>EP_SHIFT;
	}
	
	 public void make(int move) {
		
		 int oldCsState[] = new int[2];
		 int newCsState[] = new int[2];
		 
		 oldCsState[WHITE] = flags  & 3;
		 oldCsState[BLACK]=  ( flags >> 2 ) & 3;
				
		 this.seventhRankExtension=false;
		 this.promotion=false;
		 moveHistory[moveCounter]= move;
		 capturedPieces[moveCounter]= -1;
		
		 
		 int to = ((move >> TO_SHIFT) & SQUARE_MASK);		
		 int from = move & SQUARE_MASK; 
		 int pieceMoving = (move >> PIECE_SHIFT) & PIECE_MASK;		
		 //int type = (move >> TYPE_SHIFT) & TYPE_MASK;	
		 
		 long fromBB    =   1L <<   ( 63 - from);
		 long  toBB     =   1L <<   ( 63 - to);
		 long fromToBB = fromBB ^ toBB; 
		
		 board[from] = EMPTY;
	
		 int movingPieces=12+color;
		 boolean resetEP=false;
		 int oldEPSquare = flags >>EP_SHIFT;
		 int newEPSquare = -1;
		 
		 boolean resetCastling[] = new boolean[2];
		 resetCastling[0]=false;
		 resetCastling[1]=false;
		 int victim;
		 int moveType = Move2.moveType(move);
		
		 if (moveType != Move2.doublePawnMove) {
		    	if (oldEPSquare != 0) {
					newEPSquare=0;
					flags = (flags & EP_CLEAR); // Clear the  value
			    	flags = (flags | (newEPSquare << EP_SHIFT)); // Change the value and return the new irrev state integer
					resetEP=true;
				}
		 }
		 else {
			    int[] offset = { -8, 8};
			    int epSquare2 = from + offset[color];
				newEPSquare = 63 - epSquare2;
				flags = (flags & EP_CLEAR); // Clear the  value
				flags = (flags | (newEPSquare << EP_SHIFT)); // Change the value and return the new irrev state integer
				resetEP=true;
		 }
		 
		 switch(moveType) {
		       
		    case Move2.singlePawnMove:
		    	int[] lowerBound = {8, 48};
		    	int[] upperBound = { 15, 55};
		    	
		    	if ( to >= lowerBound[color] && to <= upperBound[color])
		    		seventhRankExtension=true;
		    		
		    	int posValue =  Eval.pieceTables[pieceMoving][to]  -  Eval.pieceTables[pieceMoving][from] ;
		    	positional[color] +=posValue; 
		    	
		    	board[to] =  pieceMoving;
		    	bitboard[pieceMoving]  ^=  fromToBB;   

		    	bitboard[movingPieces]                   ^=  fromToBB;   // update occupied ...
		    	bitboard[ALLPIECES]                      ^=  fromToBB;   // update occupied ...
		    
				hash=  hash ^  pieces[pieceMoving][from];
				hash=  hash ^  pieces[pieceMoving][to];
		    break;
			
		    case Move2.simple: case Move2.doublePawnMove:
		    	if (pieceMoving < 6 ) {
		    		posValue =  Eval.pieceTables[pieceMoving][to]  -  Eval.pieceTables[pieceMoving][from] ;
		    		positional[color] +=posValue;
		    	}
		    	
		    	board[to] =  pieceMoving;
		    	bitboard[pieceMoving]  ^=  fromToBB;   

		    	bitboard[movingPieces]                   ^=  fromToBB;   // update white or black color bitboard
		    	bitboard[ALLPIECES]                      ^=  fromToBB;   // update occupied ...
		   
		    	hash=  hash ^  pieces[pieceMoving][from];
				hash=  hash ^  pieces[pieceMoving][to];
		    	break;
		    
		    case Move2.kcastle: 
		 		int[] ksFromRook={63, 7};
		 		int[] ksToRook  ={61, 5};
		 		
		 		long[] ksRookFrom={1L , 72057594037927936L  };
		 		long[] ksRookTo={ 4L , 288230376151711744L };

		 		positional[color] += CASTLING_BONUS;
		 		board[to] =  pieceMoving;
		 		
		 		int rookFrom  = ksFromRook[color];
		 		int rookTo   =  ksToRook[color];
		 		
		 		board[rookFrom] =  EMPTY;
		 		board[rookTo] =  castleRook[color];

		 		bitboard[pieceMoving]  ^=  fromToBB;

		 		bitboard[movingPieces]                       ^=  fromToBB;   // update white or black color bitboard
		 		bitboard[ALLPIECES]                      ^=  fromToBB;   // update occupied ...

		 		long rookFromToBB = ksRookFrom[color] ^ ksRookTo[color]; 

		 		int theRook = castleRook[color];
		 		
		 		bitboard[theRook]  ^=  rookFromToBB; 
		 		bitboard[12+color]  ^=  rookFromToBB;
		 		bitboard[ALLPIECES] ^=  rookFromToBB;   // update occupied ...

		    	hash=  hash ^  pieces[pieceMoving][from];
				hash=  hash ^  pieces[pieceMoving][to];
			
				hash=  hash ^  pieces[theRook][rookFrom];
				hash=  hash ^  pieces[theRook][rookTo];
		 		break;
		 		
		 	 case Move2.qcastle:   
		 		int[] qsFromRook={56, 0};
		 		int[] qsToRook  ={59, 3};
		 		
		 		long[] qsRookFrom={128L , -9223372036854775808L  };
		 		long[] qsRookTo={ 16L , 1152921504606846976L };
		 		
		 		positional[color] += CASTLING_BONUS;
		 		board[to] =  pieceMoving;
		 		
		 		rookFrom  = qsFromRook[color];
		 		rookTo   =  qsToRook[color];
		 		
		 		board[rookFrom] =  EMPTY;
		 		board[rookTo] =  castleRook[color];

		 		bitboard[pieceMoving]  ^=  fromToBB;

		 		bitboard[movingPieces]                       ^=  fromToBB;   // update white or black color bitboard
		 		bitboard[ALLPIECES]                      ^=  fromToBB;   // update occupied ...

		 		rookFromToBB = qsRookFrom[color] ^ qsRookTo[color]; 

		 		theRook = castleRook[color];
		 		bitboard[theRook]  ^=  rookFromToBB; 
		 		bitboard[12+color]  ^=  rookFromToBB;
		 		bitboard[ALLPIECES] ^=  rookFromToBB;   // update occupied ...

		 		flags = flags & kingSideFlags[color];
		 		flags = flags & queenSideFlags[color];

		    	hash=  hash ^  pieces[pieceMoving][from];
				hash=  hash ^  pieces[pieceMoving][to];
			
				hash=  hash ^  pieces[theRook][rookFrom];
				hash=  hash ^  pieces[theRook][rookTo];
		 		break;
		 	
		 
		 	case Move2.captureNoPromotion: 
		 		
		 		victim = (move >> CAPTURE_SHIFT) & PIECE_MASK ;
		 		
		 		capturedPieces[moveCounter]= victim;
		 		
		 		int posVal;
		 		if (pieceMoving < 6 ) {
		 			posVal =  Eval.pieceTables[pieceMoving][to]  -  Eval.pieceTables[pieceMoving][from] ;
					positional[color] +=posVal; 
		 		}
		 		
		 		if (victim < 6 )  positional[1-color] -= Eval.pieceTables[victim][to];
		 		
		 		board[to] =  pieceMoving;
		 		bitboard[pieceMoving]                 ^=  fromToBB; 
		 		bitboard[movingPieces]                ^=  fromToBB;   // update white or black color bitboard
		 		bitboard[victim] 					  ^=  toBB;       // clear bit of the captured piece
		 		bitboard[12 + (1-color)]             ^=  toBB;       // clear bit of the captured piece
		 		bitboard[ALLPIECES] = bitboard[WHITEPIECES] | bitboard[BLACKPIECES];
		 	
		 		hash=  hash ^  pieces[pieceMoving][from];
				hash=  hash ^  pieces[pieceMoving][to];
				hash=  hash ^  pieces[victim][to];
			
		 		 material[ 1-color] -= valueMap[victim];
		 		 break;
		 	
		 	case Move2.capturePromotionQueen: 
		 		
		 		int pawn[]={WP, BP};
		 		
		 		int promotionPiece=pp[color];
		 		
		 		victim = (move >> CAPTURE_SHIFT) & PIECE_MASK ;	
		 		positional[color] -= 50;
				board[to] =  pp[color];
				capturedPieces[moveCounter]= victim;

				long notFromBB=~fromBB;

				bitboard[pawn[color]] &=  notFromBB;    //clear bit of pieceMoving
				bitboard[movingPieces] &=  notFromBB;    //clear bit of myPieces

				bitboard[victim] ^=  toBB;       // toggle bit of the captured piece
				bitboard[12+ (1-color)] ^= toBB;     // toggle bit of his pieces

				bitboard[promotionPiece] |=  toBB;   // set bit of promotion piece
				bitboard[movingPieces]  |=  toBB;       // set bit of mypieces 

				bitboard[ALLPIECES] =   bitboard[WHITEPIECES] |  bitboard[BLACKPIECES];
				
				if (victim < 6) positional[(1-color)] -= Eval.pieceTables[victim][to];
				
				hash=  hash ^  pieces[pieceMoving][from];
				hash=  hash ^  pieces[promotionPiece][to];
				hash=  hash ^  pieces[victim][to];
			
				promotion=true;
				material[1-color] -= valueMap[victim];
				material[color] += ( QUEEN_VALUE-PAWN_VALUE);
		 		break;
		 	
		 	case Move2.simplePromotionQueen: 
		 		
		 		positional[color] -=50;
		 		
		 		promotionPiece=pp[color];
		 		board[to] =  promotionPiece;
		 		
		 		notFromBB = ~fromBB;
		 		bitboard[pieceMoving] &=  notFromBB;    //clear bit
		 		bitboard[movingPieces] &=  notFromBB;    //clear bit

		 		bitboard[promotionPiece] |=   toBB;   // set bit 
		 		bitboard[movingPieces]    |=   toBB;
		 		bitboard[ALLPIECES]   =  bitboard[WHITEPIECES] | bitboard[BLACKPIECES];

		 		this.promotion=true;
		 		hash=  hash ^  pieces[pieceMoving][from];
				hash=  hash ^  pieces[promotionPiece][to];
			
				material[color] += ( QUEEN_VALUE-PAWN_VALUE);
		 		break;
		 		
		 	case Move2.epCapture: 
		 		
		 		 int [] offset2={8, -8};
		 		 int otherPieces=12 + (1-color);
		 		 int newto= to + offset2[color];
		 		
				 board[newto]= EMPTY;
				 board[to] =  pieceMoving;
				 
		 		 victim = (move >> CAPTURE_SHIFT) & PIECE_MASK ;
		 		 positional[(1-color)] -= Eval.pieceTables[victim][newto];
		 		 capturedPieces[moveCounter]= victim;
		 		 
		 		 posVal =  Eval.pieceTables[pieceMoving][to]  -  Eval.pieceTables[pieceMoving][from] ;
				 positional[color] +=posVal; 
				 int idx = 63 -  newto;
				 long z = ~(1L << idx);
				 
				 bitboard[pieceMoving]  			   ^=  fromToBB; 
				 bitboard[movingPieces]                ^=  fromToBB;   // update white or black color bitboard
				 
				 bitboard[victim]                      &=  z;  // clear bit
				 bitboard[otherPieces]                 &=  z;  // clear bit
				 
				 bitboard[ALLPIECES]= bitboard[WHITEPIECES] | bitboard[BLACKPIECES];
			
				 hash=  hash ^  pieces[pieceMoving][from];
				 hash=  hash ^  pieces[pieceMoving][to];
				 hash=  hash ^  pieces[victim][newto];
			
				 material[(1-color)] -= PAWN_VALUE;
		 		break;
		 	default :
		 		 System.out.println("Illegal move type in make");
		 		
				 System.out.println(Move2.toString(move));
				 System.out.println("movetype " + Move2.moveType(move));
				 System.out.println("piecemoving " + Move2.pieceMoving(move));
				 System.out.println("victim " + Move2.capture(move));
				 break;
		 }
		 moveCounter++;
		 if (resetEP) {
				hash = hash ^ en_passant[oldEPSquare];
				hash = hash ^ en_passant[newEPSquare];
		 }
		
		 for (int i=0; i< 2; i++) {
			 if ( board[rookHomeKS[i]] != castleRook[i] ) {
				 flags = flags & kingSideFlags[i];
			 }
			 if ( board[rookHomeQS[i]] != castleRook[i] ) {
			     flags = flags & queenSideFlags[i];
			 }
		 }
		
		 if ( board[kingHome[color]] != king[color]) {
			 flags = flags & kingSideFlags[color];
			 flags = flags & queenSideFlags[color];
		 }
		 newCsState[WHITE] = flags  & 3;
		 newCsState[BLACK]=  ( flags >> 2 ) & 3;
		 
		 if (newCsState[WHITE] != oldCsState[WHITE]) {
			
				 hash =  hash ^ W_CASTLING_RIGHTS[oldCsState[WHITE]];
				 hash =  hash ^ W_CASTLING_RIGHTS[newCsState[WHITE]];
		 }
		 if (newCsState[BLACK] != oldCsState[BLACK]) {
				 hash =  hash ^ B_CASTLING_RIGHTS[oldCsState[BLACK]];
				 hash =  hash ^ B_CASTLING_RIGHTS[newCsState[BLACK]];
			 
		 }
		 if (color==BLACK) hash = hash ^  side;
		 
	
		 color=1-color;
	 }
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		GameState other = (GameState) obj;
		if (!Arrays.equals(bitboard, other.bitboard))
			return false;
		if (!Arrays.equals(board, other.board))
			return false;
		if (!Arrays.equals(positional, other.positional))
			return false;
		if (isBitSet(flags,WKSIDE) != isBitSet(other.flags,WKSIDE))  return false;
		if (isBitSet(flags,WQSIDE) != isBitSet(other.flags,WQSIDE))  return false;
		if (isBitSet(flags,BKSIDE) != isBitSet(other.flags,BKSIDE))  return false;
		if (isBitSet(flags,BQSIDE) != isBitSet(other.flags,BQSIDE))  return false;
		if ( EnPassant.getEPSquare(flags) != EnPassant.getEPSquare(other.flags)) return false;
		
		if (color != other.color)
			return false;
		return true;
	}

	public GameState(String fen2) {
			// fen is like r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1
			
			String fen=fen2.trim();
			String[] s = fen.split("\\s+");
		
			String rows[]= s[0].split("\\/");
			
			char[] board1 = Util.convertRowsToBoard(  rows);
			board = Util.convertCharBoardToIntBoard(board1);
			//boolean white;
			if (s[1].equals("w")) {
				color=WHITE;
			}
			else {
				color=BLACK;
			}
			
			fenToCastlingPrivilege(s);
			
			String ep=s[3];
			
			if (ep.equals("-")) {
				flags = EnPassant.setEnPassantValue(flags, 0);
			}
			else {     // like e3 or d6
				flags = EnPassant.setEnPassantValue(flags,  63 - Util.getIndexFromSquare(ep));
			}
			
			String[] bitStrings = BitUtil.boardToBitStrings(board1);
			
			for (int i=0; i < 12; i++) 
			  bitboard[i]= BitUtil.BitString2Long(bitStrings[i]);
			
			bitboard[WHITEPIECES]= bitboard[WP] | bitboard[WN] | bitboard[WB] | bitboard[WR] | bitboard[WQ] | bitboard[WK];
			bitboard[BLACKPIECES]= bitboard[BP] | bitboard[BN] | bitboard[BB] | bitboard[BR] | bitboard[BQ] | bitboard[BK];
			bitboard[ALLPIECES] = bitboard[WHITEPIECES] | bitboard[BLACKPIECES];
			material[WHITE]=this.materialValue(WHITE);
			material[BLACK]=this.materialValue(BLACK);
			
			for (int i = 0; i < 6; i++)  {
				if (i % 2 == 0) {
					positional[WHITE] += addPositionalValueForType( i); 
				}
				else {
					positional[BLACK] += addPositionalValueForType( i); 
				}
			}
			
			hash= hash();
			
		}

	private void fenToCastlingPrivilege(String[] s) {
		 for (int i=0; i < 4; i++)
		   flags =  clearBit(flags,i);
		
		 
		String castlingS=s[2];
		if (castlingS.equals("-")) {
			//do nothing
		}
		else {
			if (castlingS.contains("K")) {
				  flags = setBit(flags,WKSIDE);
			}
			if (castlingS.contains("Q")) {
				  flags =  setBit(flags,WQSIDE);
			}
			if (castlingS.contains("k")) {
				  flags = setBit(flags,BKSIDE);
			}
			if (castlingS.contains("q")) {
				  flags = setBit(flags,BQSIDE);
			}
		}
	}
	  
	  public void print() {
		    System.out.println("***************** start ***************************************");
			System.out.println(toString());
			System.out.println("***************** end ***************************************");
		}

		@Override
		public String toString() {
			return "GameState2  : color =[" 
					+ color + ","
					+ " epSquare=" + EnPassant.getEPSquare(flags) + ","
					+ "castlingPrivilege="
					+ castlingToString() +
					" white material " + material[WHITE] +
					" black material " + material[BLACK] +
					" white positional " + positional[WHITE] +
					" black positional " + positional[BLACK] +
					BitUtil.boardToString(bitboard) +
					"]";
		}
		
		
	private int  getPositionalValue(int i, int piece) {
			return  Eval.pieceTables[piece][i];	
	}
	
		private int addPositionalValueForType ( int piece) {
			long x = bitboard[piece];
			int total=0;
			int idx = Long.numberOfTrailingZeros(x);
			
			while (idx < 64)
			{
				total += getPositionalValue( 63 - idx, piece);
				x = x & ( x - 1 );
				idx = Long.numberOfTrailingZeros(x);
			}  
			return total;
		}
	
		private int twoBishopBonus(int color) {
			int myCount = Long.bitCount(bitboard[WB+color]);
			int hisCount = Long.bitCount(bitboard[WB+(1-color)]);
			if (myCount == 2 && hisCount < 2) {
				return TWO_BISHOPS_BONUS;
			}
			return 0;
		}
		public int  bonus( int color) {

			int bonus=0;

			if (material[WHITE]  + material[BLACK] >= ENDGAME_MATERIAL_VALUE) 
			{    
				bonus = openingMidgameBonus(color, bonus);
					
			}
			else {    // specific to endgame
				bonus = endgameBonus(color, bonus);
			}
			
			if (material[1-color] - material[color] >= 200) // we are down in material at least two pawns
			{
				bonus = materialDownBonus(color, bonus);
			}
			bonus += twoBishopBonus(color);
			if (( bitboard[WR+color] & seventhRank[color]) != 0) {
				bonus += ROOK_ON_SEVENTH_BONUS;
			}
			//bonus += tropismPenalty(color);
			
			//System.out.println("bonus " + bonus);
			
			return bonus;
		}
		
	/*	
		private int rookEvaluation(int color) {
			int bonus=0;
			long rooks=bitboard[WR+color];
			long pawns = bitboard[WP+color];
			
			long hash= rooks | pawns;
			if ( rookTable.exists(hash,rooks)) {
				return rookTable.getScore(hash);
			}
			
			int idx = Long.numberOfTrailingZeros(rooks);
			while (idx < 64)
			{
				
				bonus = bonus + ROOK_OPEN_OR_HALF_OPEN_FILE_BONUS * BitUtil.isOnOpenOrHalfOpenFile(rooks, pawns);
				
				rooks = rooks & ( rooks - 1 );
				idx = Long.numberOfTrailingZeros(rooks);
			} 
			rookTable.store(hash, rooks, bonus);
			return bonus;
		}*/

		private int openingMidgameBonus(int color, int bonus) {
			
			if  (  (  bitboard[10+color] &  ksCastled[color])    != 0 ) 
			{
				long x = Eval.kingsideMask[color] & bitboard[WP+color];
				
				if (!Eval.goodKingsideShield(x, color)) 
				{
				  //kingside castled with kside pawn shield
				//penalty for moving pawns away from the shield in front of castled king on kingside
				//System.out.println("wingPawnPushPenalty kingside = -35");
				  bonus += GameState.WING_PAWN_PUSH_PENALTY;
				}
				if (( bitboard[WP+color] &   wreckedQSPawnShield[color]) != 0 ) {  // if castled kingside, definitely push QSIDE pawns
					 bonus += GameState.WING_PAWN_PUSH_BONUS;
				}
			}
					
			
			else
				
				if  (  (  bitboard[10+color] &  qsCastled[color])    != 0 ) 
				{
					if (( bitboard[WP+color] &   wreckedQSPawnShield[color]) != 0 ) 
					{
					//penalty for moving pawns away from the shield in front of castled king on kingside
					//System.out.println("wingPawnPushPenalty qside  = -35");
					   bonus += GameState.WING_PAWN_PUSH_PENALTY;
				    }
					if (( bitboard[WP+color] &   wreckedKSPawnShield[color]) != 0 ) {  // if castled queensider, definitely push KSIDE pawns
						 bonus += GameState.WING_PAWN_PUSH_BONUS;
					}
				}
			
			if (  (bitboard[10+color] & safeMiddleGameKingSquares[color]) == 0) {  //oops, king took a walk before the endgame
						//System.out.println("kingWalkPenalty = - 35");
						bonus += GameState.KING_WALK_PENALTY;
			}
			return bonus;
		}

		private int endgameBonus(int color, int bonus) {
			//endgame bonus for centralized king
			int idx = Long.numberOfTrailingZeros(bitboard[WK+color]);
			int ktegBonus = Eval.kingTableEndGame[idx];
			bonus += ktegBonus;
			//System.out.println("King table endgame bonus " + ktegBonus);
			// encouragement to push pawns in endgame
			long[][] pawnRanks = 
				{{ Movegen.RANK4, Movegen.RANK5},
					{ Movegen.RANK5, Movegen.RANK4} };
			int[] pawnBonus = { 20, 30};
			int[]pawnCountForRank = new int[2];
			
			for (int i=0; i < 2; i++) {
				pawnCountForRank[i] = Long.bitCount(bitboard[WP+color] & pawnRanks[color][i]);
				int egPawnPushBonus = pawnCountForRank[i]*pawnBonus[i];
				//System.out.println("endgame pawn push bonus" + egPawnPushBonus);
				bonus += egPawnPushBonus;
			}
			return bonus;
		}

		private int materialDownBonus(int color, int bonus) {
			//ideas from : http://www.mayothi.com/nagaskakichess4.html
			
			//encourage drawish stuff:
			//bishops of opposite color
			int enemyBishopCount = Long.bitCount(bitboard[WB+( 1 -color)]);
			int bishopCount = Long.bitCount(bitboard[WB+ color]);
			
			if (bishopCount== 1 && enemyBishopCount== 1) {
				if (( (bitboard[WB+color] & Movegen.lightSquares) != 0) &&
				    ( (bitboard[WB+(1-color)] & Movegen.darkSquares) != 0)) {
					//System.out.println("Bishops of opp color bonus = 50");
					bonus += 50;
				}
				else
					if (( (bitboard[WB+color] & Movegen.darkSquares) != 0) &&
						    ( (bitboard[WB+(1-color)] & Movegen.lightSquares) != 0)) {
						//System.out.println("Bishops of opp color bonus = 50");
							bonus += 50;
						}
			}
			// the fewer the opponents pawns, when we are down,  the better
			int enemyPawnCount = Long.bitCount(bitboard[WP+(1-color)]);
			int pawnRemovalBonus =  ( 8 - enemyPawnCount)*PAWN_REMOVAL_BONUS;
			//System.out.println("pawn Removal bonus " + pawnRemovalBonus);
			bonus += pawnRemovalBonus;
			
			//Give a bonus for two rooks (two rooks can be drawish) 
			int rookCount = Long.bitCount(bitboard[WR+color]);
			
			if (rookCount ==2)  {
				//System.out.println("rook count bonus = 20 ");
				bonus += ROOK_COUNT_BONUS;
			}
			
			//Give a bonus for keeping the queen (the queen tend to make things more difficult for the opponent)
			int queenCount = Long.bitCount(bitboard[WQ+color]);
			if (queenCount ==1) {
				//System.out.println("queen count bonus = 20 ");
				bonus += QUEEN_COUNT_BONUS;
			}
			return bonus;
		}
	
		public  int[]  pawnStructureBonus() {
				//passers are more valuable as the material decreases and as their rank increases
			
			int[] bonusByRank={ 0, 4, 6, 8, 10, 12, 14, 16 };  // for passed pawns
			
			long whitePawns=bitboard[WP];
			long blackPawns=bitboard[BP];
			
			long hash= bitboard[WP] | bitboard[BP];
			if ( pawnStructureTable.exists(hash, whitePawns, blackPawns)) {
				return pawnStructureTable.getScore(hash);
			}
		
			int totalMaterial= material[0] + material[1];    //4325 is max , below 3000 is endgame
			
			
			//PASSED PAWN BONUS
			int x=16;
			if (totalMaterial > 4000) {
				x=8;
			}
			else 
				if (totalMaterial > 3000 && totalMaterial <=4000 )  {
					x=10;
				}
				else
					if (totalMaterial > 2000 && totalMaterial <=3000 )  {
						x=12;
					}
					else 
						if (totalMaterial > 1000 && totalMaterial <=2000 )  {
						x=14;
					}
			
			long[] passers = new long[2];
			
			passers[0]= BitUtil.passedPawnsWhite(whitePawns, blackPawns);
			
			passers[1]= BitUtil.passedPawnsBlack(whitePawns, blackPawns);
		
			int passedPawnBonus[]= new int[2];
			int y,ppBonus,rank;
			
			for (int i=0; i < 2; i++) {
				int idx = Long.numberOfTrailingZeros(passers[i]);
				while (idx < 64){
					rank =  (8 - idx/8);
					if (i == 0) rank = 9-rank;
				
					y=rank*bonusByRank[rank];
					ppBonus= x+ y;
					passedPawnBonus[i] +=ppBonus;
					
					passers[i] = passers[i] & ( passers[i] - 1 );
					idx = Long.numberOfTrailingZeros(passers[i]);
				} 
			}
			int[]bonus= new int[2];
			bonus[0] = passedPawnBonus[0];
			bonus[1] =  passedPawnBonus[1];
			
			//END PASSED PAWN BONUS
			
			/*int[] isolated = new int[2];  // number of isolated pawns, white black
			int[] doubled = new int[2];
			
			isolated[0] = BitUtil.isolatedPawnsWhite(whitePawns, blackPawns);
			bonus[0] += isolated[0]*ISOLATED_PAWN_PENALTY;
			
			isolated[1] = BitUtil.isolatedPawnsBlack(whitePawns, blackPawns);
			bonus[1] += isolated[1]*ISOLATED_PAWN_PENALTY;
			
			doubled[0]= BitUtil.doubledPawnsWhite(whitePawns);
			doubled[1]= BitUtil.doubledPawnsBlack(blackPawns);
			
			int whiterookCount = Long.bitCount(bitboard[WR]);
			bonus[0] += doubled[0]*doubledPawnPenalty(whiterookCount);
			
			int blackrookCount = Long.bitCount(bitboard[BR]);
			bonus[1] += doubled[1]*doubledPawnPenalty(blackrookCount);*/
			
			pawnStructureTable.store(hash, whitePawns, blackPawns, bonus[0], bonus[1]);
			return passedPawnBonus;
		}
		
		/*private int  doubledPawnPenalty(int rookCount) {
			switch(rookCount) {
				case 2: return -10;
				case 1: return -15;
				case 0: return -20;
				default : return 0;
			}
		}*/
		public int getEvaluation() {

			int[] tot= new int[2];
			int[] passedPawnBonus= this.pawnStructureBonus();
			//System.out.println("WHITE PP BONUS : " + passedPawnBonus[0] + " BLACK PP BONUS " + passedPawnBonus[1]);
			tot[0] = material[0]
					+ positional[0]
					+ bonus(0) 
					+ passedPawnBonus[0];
			
			tot[1] = material[1] 
					+ positional[1]
					+ bonus(1)
					+ passedPawnBonus[1];
			int eval =  tot[color] - tot[1-color];
			return eval; 
			
		}
		public int getEvaluationMaterial() {

			int[] tot= new int[2];
			
			tot[0] = material[0];
					
			
			tot[1] = material[1] ;
				
			int eval =  tot[color] - tot[1-color];
			return eval; 
			
		}
		
		public boolean isEndGame() {
		  return (material[WHITE]  + material[BLACK] < ENDGAME_MATERIAL_VALUE)? true: false;
		}
	
		public long getHash() {
			return this.hash;
		}

		public  int materialValue(int color) {      // only used for initializing a state  , not used after make
			int pawnVal =   PAWN_VALUE * ( Long.bitCount(bitboard[WP+color])  );
			int knightVal = KNIGHT_VALUE * ( Long.bitCount(bitboard[WN+color])  );
			int bishopVal = BISHOP_VALUE * ( Long.bitCount(bitboard[WB+color]));
			int rookVal =   ROOK_VALUE * ( Long.bitCount(bitboard[WR+color])  );
			int queenVal =  QUEEN_VALUE * ( Long.bitCount(bitboard[WQ+color])  );
			int val = pawnVal + knightVal + bishopVal + rookVal + queenVal;
			return val;
		}
	
		public int getMaterialValue() {    //just used for testing
				return materialValue(color) - materialValue(1-color);
		}
		
		//MOVE GENERATION
	
	
		//***********************************************************************************************
		
		
		private int  getAllWhiteMoves(int cnt, int[] moves, long all, long whitePieces, long blackPieces, long[] bitboard) {
		
			cnt = Movegen.getAllPawnMoves(cnt, moves, bitboard[WP], all, blackPieces, WHITE, WP, flags, board); 
			cnt =Movegen.getMovesForAllKnights(cnt,moves, bitboard[WN], whitePieces,WN, board);
			cnt =Movegen.getMovesForAllBishops(cnt,moves,bitboard[WB],all,whitePieces,WB, board);
			cnt = Movegen.getMovesForAllRooks(cnt,moves,bitboard[WR],all,whitePieces,WR, board);
			cnt = Movegen.getMovesForAllQueens(cnt,moves,bitboard[WQ],all,whitePieces,WQ, board);
			cnt =Movegen.getMovesForTheKing( cnt,moves,bitboard[WK],whitePieces,WK, board);
		
			if (  canCastle(WKSIDE, all)) {
			
				int move = Move2.createMove(WK, 60, 62,0, Move2.kcastle,  0)	;
				moves[cnt++]=move;
			}
			if (  canCastle(WQSIDE, all)) {
				
				int move = Move2.createMove(WK, 60, 58,0, Move2.qcastle,  0)	;
				moves[cnt++]=move;
			}
			return cnt;
		}
		
		private int getAllBlackMoves(int cnt, int[] moves , long all, long whitePieces, long blackPieces, long[] bitboard) {
			cnt = Movegen.getAllPawnMoves(cnt,moves,  bitboard[BP], all, whitePieces, BLACK, BP, flags, board); 
			cnt = Movegen.getMovesForAllKnights(cnt,moves, bitboard[BN],blackPieces,BN, board );
			cnt = Movegen.getMovesForAllBishops(cnt,moves, bitboard[BB],all,blackPieces,BB, board);
			cnt = Movegen.getMovesForAllRooks(cnt,moves, bitboard[BR],all,blackPieces,BR, board);
			cnt = Movegen.getMovesForAllQueens(cnt,moves, bitboard[BQ],all,blackPieces,BQ, board);
			cnt = Movegen.getMovesForTheKing(cnt,moves,bitboard[BK],blackPieces,BK, board);
			
			if (  canCastle(BKSIDE, all))  {
				int move = Move2.createMove(BK, 4, 6,0, Move2.kcastle,  0)	;
				moves[cnt++]=move;
			}
			if (  canCastle(BQSIDE, all) ) {
				
				int move = Move2.createMove(BK, 4, 2, 0, Move2.qcastle,  0)	;
				moves[cnt++]=move;
			}
			return cnt;
		}
		
		public int[] generateNonCaptures(int color ) {
			int[] moves = new int[150];
			int cnt=0;
		
			long all=this.bitboard[ALLPIECES];
			
			cnt = Movegen.getPawnPushes(cnt, moves, bitboard[WP +color], all, color, WP+color); 
			
			cnt = Movegen.getKnightNonCaptures(cnt,moves,bitboard[WN+color], ~all, WN+color);
			cnt = Movegen.getBishopNonCaptures(cnt,moves,bitboard[WB+color],all, ~all, WB+color);
			cnt = Movegen.getRookNonCaptures(cnt,moves,bitboard[WR+color],all, ~all, WR+color);
			cnt = Movegen.getQueenNonCaptures(cnt,moves,bitboard[WQ+color],all, ~all, WQ+color);
			cnt =Movegen.getKingNonCaptures(cnt,moves,bitboard[WK+color], ~all,WK+color);
			
			if (color == BLACK) {
				if (  canCastle(BKSIDE, all))  {
					int move = Move2.createMove(BK, 4, 6,0, Move2.kcastle,  0)	;
					moves[cnt++]=move;
				}
				if (  canCastle(BQSIDE, all) ) {
					
					int move = Move2.createMove(BK, 4, 2, 0, Move2.qcastle,  0)	;
					moves[cnt++]=move;
				}
			}
			else {
				if (  canCastle(WKSIDE, all)) {
					
					int move = Move2.createMove(WK, 60, 62,0, Move2.kcastle,  0)	;
					moves[cnt++]=move;
				}
				if (  canCastle(WQSIDE, all)) {
					
					int move = Move2.createMove(WK, 60, 58,0, Move2.qcastle,  0)	;
					moves[cnt++]=move;
				}
			}
			
			int[] moves2 = new int[cnt];
			System.arraycopy(moves, 0, moves2, 0, cnt );
			return moves2;
		}
		
		public int[] generateCapturesAndPromotions(int color ) {
			int[] moves = new int[150];
			int cnt=0;
		
			long all=this.bitboard[ALLPIECES];
			long enemyPieces=this.bitboard[WHITEPIECES+(1-color)];
			
			cnt = Movegen.getPawnCapturesAndPromotions(cnt, moves, bitboard[WP +color], all, enemyPieces, color, WP+color, flags, board); 
			cnt =Movegen.getKnightCaptures(cnt,moves, bitboard[WN+color], enemyPieces,WN+color, board);
			
			cnt =Movegen.getBishopCaptures(cnt,moves,bitboard[WB+color],all,enemyPieces,WB+color, board);
			cnt = Movegen.getRookCaptures(cnt,moves,bitboard[WR+color],all,enemyPieces,WR+color, board);
			cnt = Movegen.getQueenCaptures(cnt,moves,bitboard[WQ+color],all,enemyPieces,WQ+color, board);
			cnt =Movegen.getKingCaptures( cnt,moves,bitboard[WK+color],enemyPieces,WK+color, board);
			
			int[] moves2 = new int[cnt];
			System.arraycopy(moves, 0, moves2, 0, cnt );
			return moves2;
		}
		

		public int[] getAllMoves() {
			
			int[] moves = new int[200];
			int cnt=0;
			
			if (color==WHITE) {
				cnt = getAllWhiteMoves( cnt, moves,
						this.bitboard[ALLPIECES],
						this.bitboard[WHITEPIECES],
						this.bitboard[BLACKPIECES] , this.bitboard);
			}
			else {
				cnt = getAllBlackMoves(cnt, moves,
						this.bitboard[ALLPIECES],
						this.bitboard[WHITEPIECES],
						this.bitboard[BLACKPIECES], this.bitboard);
				
			}
			
		    int[] out = new int[cnt];
			System.arraycopy(moves, 0, out, 0, cnt );
			return out;
		}
		private   boolean canCastle(int index, long allPieces) {
			/*To check whether castling is legal, we have to check whether
			the squares between the King and the Rook in question, are empty. 
			This is easily done by just ANDing a pre- computed
			bitboard of these squares with the occupied bitboard.
			
			Also, you cannot castle out of check
			*/
			
			if  (!( ( ( flags >> index ) & 1 ) != 0 )) //bit is not set
				return false;
			long x = castlingBitboards[index] & allPieces;
			if ( x == 0) return true;
			return false;
		}
		//mined squares are the squares over which a King cannot travel if they are checked by the opponent
	
		public  boolean isInCheck( int color) {
				return isInCheck(bitboard[WK+color], 1-color);		
		}
		
		public  boolean isInCheck( long king, int color) {    //color is the guys who are checking the king
			
			int idx = Long.numberOfTrailingZeros(king);
			
			long x = Movegen.knightMoveArray[idx];
			if ( (x & bitboard[WN + color]) != 0 ) return true;
			if  (( Movegen.pawnCheckArray[color][idx] & bitboard[WP + color]) != 0 ) return true;
			
			x = Movegen.kingMoveArray[idx];
			if ( (x & bitboard[WK + color]) != 0 ) {
				return true;
			}
			long queen=bitboard[WQ+color];
			
			long allPieces=bitboard[ALLPIECES];
			x = BishopMagic.getBishopAttacks(idx, allPieces);
			if ((x & ( bitboard[WB+color] | queen) )!= 0) return true;
		
			x = RookMagic.getRookAttacks(idx,allPieces);
			if ((x & ( bitboard[WR+color] | queen))!= 0) return true;
			
			return false;
		}
		
		public String castlingToString() 
		{
			StringBuffer sb= new StringBuffer();
			boolean flag=false;
			for (int i=0; i < 4; i++) 
			{
				if (isBitSet(flags,i)) 
				{
					switch(i) {
						case WKSIDE: sb.append('K'); 
						flag=true;
						break;
						case WQSIDE: sb.append('Q'); 
						flag=true;
						break;
						case BKSIDE: sb.append('k'); 
						flag=true;
						break;
						case BQSIDE: sb.append('q'); 
						flag=true;
						break;
						
					}
				}
			}
			if (!flag) sb.append('-');
			return sb.toString();
		}
		
		static int clearBit(int x, int position)
		 {
		   int mask = 1 << position;
		   return x &  ~mask;
		 }
		static int setBit(int x, int position)
		 {
		  int mask = 1 << position;
		   return x | mask;
		 }
		
		 static boolean isBitSet(int x, int position)
		 {
		   x >>= position;
		  return (x & 1) != 0;
		 }
		 // if ( ( ( x >> position ) & 1 ) != 0 )
		 // then bit is set
		 
		 public void unmake(int move, int flags2, long hash2) {
		
			 int to = ((move >> TO_SHIFT) & SQUARE_MASK);		
			 int from = move & SQUARE_MASK; 
			 int pieceMoving = (move >> PIECE_SHIFT) & PIECE_MASK;		
			 int type = (move >> TYPE_SHIFT) & TYPE_MASK;	
			
			 long fromBB    =   1L <<   (63 -from);
			 long  toBB     =   1L <<   ( 63 - to);
			 long fromToBB = fromBB ^ toBB; 
			 moveCounter--;
			 
			 board[from] =  pieceMoving;
			 int color2=pieceMoving %2;
			 int posValue;
			 flags=flags2;
			 switch(type) {

		
			 case Move2.singlePawnMove:
			 case Move2.simple:  
		     case Move2.doublePawnMove:
				 board[to] = EMPTY;
				 
				 bitboard[pieceMoving]  		   ^=  fromToBB;         //put the moving piece back
				 bitboard[12+color2]                ^=  fromToBB;   // update white or black color bitboard
				 bitboard[ALLPIECES]               ^=  fromToBB;   // update occupied ...
				
				 if (pieceMoving < 6 ) {
				   posValue =  Eval.pieceTables[pieceMoving][from]  -  Eval.pieceTables[pieceMoving][to] ;
			       positional[color2] +=posValue; 
				 }
				
				 break;
				case Move2.kcastle: 
					int[] ksFromRook={63, 7};
			 		int[] ksToRook  ={61, 5};
			 		long[] fook = { 1L ^ 4L,  72057594037927936L ^ 288230376151711744L};
			 		
					positional[color2] -= CASTLING_BONUS;
					board[to] = EMPTY;
					board[ksToRook[color2]] = EMPTY;
					board[ksFromRook[color2]] =  WR+color2;

					bitboard[pieceMoving]  ^=  fromToBB;         //put the moving piece back
					bitboard[WHITEPIECES+color2]                    ^=  fromToBB;   // update white or black color bitboard
					bitboard[ALLPIECES]                      ^=  fromToBB;   // update occupied ...

					long rookFromToBB = fook[color2]; 
					bitboard[WR+color2]  ^=  rookFromToBB; 
					bitboard[WHITEPIECES+color2]  ^=  rookFromToBB; 
					bitboard[ALLPIECES]    ^=  rookFromToBB;   // update occupied ...
					
				 break;
				case Move2.qcastle: 
					int[] qsFromRook={56, 0};
			 		int[] qsToRook  ={59, 3};
			 		long[] fook2 = { 128L ^ 16L,  -9223372036854775808L ^ 1152921504606846976L};
			 		
					positional[color2] -= CASTLING_BONUS;
					board[to] = EMPTY;
			 		board[qsToRook[color2]] = EMPTY;
					board[qsFromRook[color2]] =  WR+color2;
					bitboard[pieceMoving]  ^=  fromToBB;         //put the moving piece back
				
					bitboard[WHITEPIECES+color2]                    ^=  fromToBB;   // update white or black color bitboard
					bitboard[ALLPIECES]                      ^=  fromToBB;   // update occupied ...
						
					rookFromToBB = fook2[color2];
					bitboard[WR+color2]  ^=  rookFromToBB; 
					bitboard[WHITEPIECES+color2]  ^=  rookFromToBB; 
					bitboard[ALLPIECES]    ^=  rookFromToBB;   // update occupied ...
					
				 break;
		
				case Move2.captureNoPromotion: 
					 
					 int victim = Move2.capture(move);
					 bitboard[pieceMoving]  ^=  fromToBB;         //put the moving piece back
					 victim = Move2.capture(move);  // ie; WP or WQ or BP, etc.
					 bitboard[victim] ^=  toBB;       // flip bit of the captured piece

					 board[to] =  victim;

					 int value = valueMap[victim];

					 bitboard[12+color2]  ^=  fromToBB;         //put the moving piece back
					 bitboard[12 + (1-color2)] ^=  toBB; 
					

					 bitboard[ALLPIECES] = bitboard[BLACKPIECES] | bitboard[WHITEPIECES];
					 
					 if (pieceMoving < 6 ) {
						   posValue =  Eval.pieceTables[pieceMoving][from]  -  Eval.pieceTables[pieceMoving][to] ;
					       positional[color2] +=posValue; 
					 }
						
					 if (victim < 6 )  positional[1-color2] += Eval.pieceTables[victim][to];
					 material[1-color2]   += value;
				
					 break;
					
				case Move2.capturePromotionQueen:
					
					 int promotionPiece=board[to];
					 bitboard[pieceMoving]  ^=  fromToBB;         //put the moving piece back
					 bitboard[12+color2]  ^=  fromToBB;       
					 
					 bitboard[pieceMoving]  ^=  toBB; 
					 bitboard[12+color2]  ^=  toBB;
					 
					 victim = Move2.capture(move);  // ie; WP or WQ or BP, etc.
					 bitboard[victim] ^=  toBB;       // toggle bit of the captured piece
					 bitboard[12+(1-color2)] ^=  toBB;       // toggle bit of the captured piece
					 
					 board[to] = victim;
					 value = valueMap[victim];
					
					 long notToBB = ~toBB;
					 bitboard[promotionPiece] &= notToBB;    //clear bit
					 bitboard[12+color2]   &= notToBB;    //clear bit
					 
					 positional[color2] += 50;
					 if (victim < 6) positional[1-color2] += Eval.pieceTables[victim][to];
					 
					 bitboard[ALLPIECES] = bitboard[BLACKPIECES] | bitboard[WHITEPIECES];
					 material[1-color2] += value;
					 material[color2] -=  ( QUEEN_VALUE-PAWN_VALUE);
					
				break;
				
				 case Move2.simplePromotionQueen:
					
					 promotionPiece=board[to];
					 board[to] = EMPTY;
					
					 notToBB = ~toBB;
					 
					 bitboard[pieceMoving]    |=    fromBB;     //put the moving piece back
					 bitboard[12+color2]      |=    fromBB; 
					 bitboard[pieceMoving]    &=    notToBB; 
					 bitboard[12+color2]      &=    notToBB; 
					 bitboard[promotionPiece] &=    notToBB;    //clear bit
					 bitboard[12+color2]      &=    notToBB;
					 bitboard[ALLPIECES] = bitboard[BLACKPIECES] | bitboard[WHITEPIECES];
					 material[color2] -=  ( QUEEN_VALUE-PAWN_VALUE);
					 
					 positional[color2] +=50;
					
					break;
				
				case Move2.epCapture:
					int [] offset2={8, -8};
			 		
			 		int newto= to + offset2[color2];
					board[to] = EMPTY;
					int idx;
					bitboard[pieceMoving]  ^=  fromToBB;   
					bitboard[12+color2]  ^=  fromToBB; 
					
					if (pieceMoving==WP)  {
						board[to+8] = BP;
						idx = 63 -  (to + 8 );
						long z = 1L << idx;
						bitboard[BP]  |=  z; 
						bitboard[BLACKPIECES]  |=  z; 
						positional[BLACK] += Eval.pieceTables[BP][newto];
						material[BLACK]   += PAWN_VALUE;
					}
					else {
						board[to-8] = WP;

						idx = 63 -  (to  - 8);
						long z = 1L << idx;
						bitboard[WP] |= z; 
						bitboard[WHITEPIECES]  |=  z;
						positional[WHITE] += Eval.pieceTables[WP][newto];
						material[WHITE]   += PAWN_VALUE;
					}
					 int posVal =  Eval.pieceTables[pieceMoving][from]  -  Eval.pieceTables[pieceMoving][to] ;
					 positional[color2] +=posVal; 
					 bitboard[ALLPIECES] = bitboard[BLACKPIECES] | bitboard[WHITEPIECES];
					
					break;
			 }
			 color=1-color;
			
			 hash=hash2;
			 currentPly--;
		 }
		
		//ZOBRIST
		//****************************************************************************************
		//zobrist
		private static long[][] pieces = new long[12][64] ;  	// [piece type][square]
		private static long[] en_passant = new long[64] ;   	 	// En passant
		public static long   side;    // Used for changing sides
		private static long[]  W_CASTLING_RIGHTS = new long[4];   // both ways, short, long and none  0,1,2,3
		private static long[]  B_CASTLING_RIGHTS = new long[4];   // both ways, short, long and none
		//zobrist
		static {
			java.util.Random random = new java.util.Random(17L);

			side = random.nextLong();
			
			for(int i = 0; i < 4; i++)
			{
			  W_CASTLING_RIGHTS[i] = random.nextLong();
			  B_CASTLING_RIGHTS[i] = random.nextLong();   
			}
			
			for (int i=0; i < 12; i++) {
					for (int k=0; k < 64; k++) {
						pieces[i][k] = random.nextLong();        // WP, 32  or BK, 54  for example
					}
			}
			
			for (int i=0; i < 64; i++) {
				en_passant[i] = random.nextLong();
			}
			
		}
		public  long hash()
		{
			long hash = 0;
		
			for(int i=0; i < 64 ; i++)
			{
				 int piece = board[i];
				 if (piece != -1)
				   hash ^= pieces[piece][i];
			}
			if( color==BLACK )
				hash = hash ^ side;
		
			int cState= flags  & 3;
			
			hash =  hash ^ W_CASTLING_RIGHTS[cState];
			
			cState= ( flags >>2) & 3;
		
			hash =  hash ^ B_CASTLING_RIGHTS[cState];
			
			
			int epSquare = flags >>EP_SHIFT;
			if (epSquare != 0)
				   hash = hash ^ en_passant[epSquare];
			return hash;
		}
	
	
		public static void main(String args[]) {
			int flags=0;
			flags = setBit(flags, 17);
			flags = setBit(flags, 18);
			
		   // flags = setBit(flags, WKSIDE);
		    //flags = setBit(flags, WQSIDE);
		   // flags = setBit(flags, BKSIDE);
		    flags = setBit(flags, BQSIDE);
		    System.out.println("flags=" + flags);
		    
			int sumac1 =  ((flags >> 2) & 3);
			//int sumac2 =  (flags << 1);
			
			System.out.println("sumac1=" + sumac1);
			//System.out.println("sumac2=" + sumac2);
		}
		
		private int[] getInBetweenSquares(int from, int to) {
			//get the indices of squares between and not including from and to
			int k=0;
			int[] out = new int[8];
			
			long inBetweenSquares = BitUtil.inBetweenWithoutLookup(from, to);
			int idx2 = Long.numberOfTrailingZeros(inBetweenSquares);
			
			while (idx2 < 64){
					//System.out.println("rook in between idx " + idx);
				    out[k++]=idx2;
				   
					inBetweenSquares = inBetweenSquares & ( inBetweenSquares - 1 );
					idx2 = Long.numberOfTrailingZeros(inBetweenSquares);
			}
			if (k > 0) {
				int[] out2 = new int[k];
				System.arraycopy(out, 0, out2, 0, k );
				return out2;
			}
			return null;
		}
		//to see if a hash move is legal before we dare to play it !
		public boolean isMoveLegal ( int move) {
			
			// we need to make sure that the squares between from and to, if they exist, are empty
			// if the pieceMoving is a rook, bishop or queen, whether this is a simple of capture move
			
			 int to = ((move >> TO_SHIFT) & SQUARE_MASK);
			 if (to < 0 || to > 63) return false;
			 
			 int from = move & SQUARE_MASK; 
			 if (from < 0 || from > 63) return false;
			 
			 int pieceMoving = (move >> PIECE_SHIFT) & PIECE_MASK;		
			 if (pieceMoving < 0 || pieceMoving > 11) return false;
			 
			 int victim = (move >> CAPTURE_SHIFT) & PIECE_MASK ;
			 int moveType = Move2.moveType(move);
			 int color2 = pieceMoving % 2;
			 if (color2 != this.color) return false;
			 
			 int[] offset = { -8, 8};
			 
			 if (board[from] != pieceMoving) return false;
				 
			 switch ( pieceMoving ) {
			 	case  WB : case BB:
			 	case  WQ : case BQ :
			 	case  WR:  case BR:
			 		int[] squares = getInBetweenSquares(from,to);
			 		if (squares != null) {
			 			for (int i=0; i < squares.length; i++) {
			 				if (board[squares[i]] != EMPTY) return false;
			 			}
			 		}
			 		break;
			 };
			 
			 switch(moveType) {
			 	case (Move2.simple) :
			 		if (board[to] != EMPTY) return false;
			 	break;
			 	
			 	case(Move2.singlePawnMove) :
			 		if (board[to] != EMPTY) return false;
			 	break;
			 	
			 	case(Move2.doublePawnMove) :
			 		
			 	     if (board[to+ offset[color2]] != EMPTY) return false;
			 		 if (board[to] != EMPTY) return false;
			 	break;
			 	
			 	case (Move2.kcastle)  :
			 		int[] foo= { WKSIDE, BKSIDE};
			 		if (! (canCastle( foo[color2], bitboard[ALLPIECES])  ))  return false;
			 	break;
			 	
			 	case (Move2.qcastle)  :
			 		int[] foo2= { WQSIDE, BQSIDE};
		 			if (! (canCastle( foo2[color2], bitboard[ALLPIECES])  ))  return false;
				break;
			 	
			 	case(Move2.simplePromotionQueen) :
			 		if (board[to] != EMPTY) return false;
			 	break;
			 	
			 	case(Move2.captureNoPromotion) :
			 		if (victim < 0 || victim > 9) return false;
			 		if (board[to] != victim) return false;
			 	break;
			 	
			 	case ( Move2.capturePromotionQueen) :
			 		if (victim < 0 || victim > 9) return false;
			 		if (board[to] != victim) return false;
			 	break;
			 	
			 	case ( Move2.epCapture) :
			 		
			 		 int epSquare2 = from + offset[color2];
				     //newEPSquare = 63 - epSquare2;
			 		 if (board[epSquare2] != victim) return false;
			 	break;
			 	default: return false;   //weird movetype
			 }
			 return true;
		}

		public static int getLastMove() {
			// TODO Auto-generated method stub
			return moveHistory[moveCounter];
		}
		//**************************************************************************************************************
		
		//CHECK EVASION MOVE GEN
		public static final int ROOK_ATTACK=0;
		public static final int BISHOP_ATTACK=1;
		
		public  int[] generateCheckEvasionMoves(int color) {
			final int[] pushDiffs                   = {8, -8};
			// note that we must still verify the legality of the king and pawn moves, after make,
			//but for the the other generated moves, this is not needed
			long all=bitboard[GameState.ALLPIECES];
			//long enemyPieces=gs.bitboard[GameState.WHITEPIECES+(1-color)];
			
			int[] moves = new int[100];
			int cnt=0;
			
			cnt =Movegen.getMovesForTheKing( cnt,moves,bitboard[GameState.WK+color],bitboard[GameState.WHITEPIECES+color],GameState.WK+color, board);
			
			// the first item in squares is the attacking piece
			// we try to generate moves which capture this piece, followed by interposing moves

			int[] squares = getCheckEvasionSquares( color);
			if (squares==null) {
				int[] out = new int[cnt];
				System.arraycopy(moves, 0, out, 0, cnt );
				return out;
			}
			
			long pawns = bitboard[GameState.WP+color];
			long bishops=bitboard[GameState.WB+color];
			long rooks=bitboard[GameState.WR+color];
			long knights=bitboard[GameState.WN+color];
			long queens=bitboard[GameState.WQ+color];
			
			long attacker = 1L <<squares[0];
			
			if (squares != null) {
				cnt=Movegen.addPawnAttacks(cnt, moves, pawns, attacker, color, WP+color, flags,  board);
				cnt =Movegen.getKnightCaptures(cnt,moves, knights, attacker,WN+color, board);
				cnt =Movegen.getBishopCaptures(cnt,moves,bishops,all,attacker,WB+color, board);
				cnt = Movegen.getRookCaptures(cnt,moves,rooks,all,attacker,WR+color, board);
				cnt = Movegen.getQueenCaptures(cnt,moves,queens,all,attacker,WQ+color, board);
			}
			// the other possible squares, if they exist, should be target squares for noncaptures to block the check
			int diffs2=pushDiffs[color];
			for (int i=1; i < squares.length; i++) {
				long interveningSquare=1L <<squares[i];
				long interveningSquare2=1L <<squares[i];
				interveningSquare2 = interveningSquare2 | ( 1L << ( squares[i] -diffs2));
				
				cnt = Movegen.getPawnPushes(cnt, moves, pawns,~interveningSquare2, color, WP+color) ;
				cnt = Movegen.getKnightNonCaptures(cnt, moves, knights, interveningSquare, WN+color) ;
				cnt = Movegen.getBishopNonCaptures(cnt, moves, bishops, all, interveningSquare, WB+color) ;
				cnt = Movegen.getRookNonCaptures(cnt,   moves, rooks, all, interveningSquare,WR+color) ;
				cnt = Movegen.getQueenNonCaptures(cnt, moves, queens, all, interveningSquare,WQ+color) ;
			}
			
			int[] out = new int[cnt];
			System.arraycopy(moves, 0, out, 0, cnt );
			return out;
		}
		
		public  int[] getCheckEvasionSquares( int color) {
			
			long allPieces = bitboard[ALLPIECES];
			long king = bitboard[GameState.WK+color];
			int kingIdx = Long.numberOfTrailingZeros(king);
			
			int[] result1 = isInCheck2( color,WR, ROOK_ATTACK, kingIdx, allPieces);
			if ( result1 != null) {
				return result1;
			}
			int[] result2 = isInCheck2( color,WB, BISHOP_ATTACK, kingIdx, allPieces);
			if ( result2 != null)  {
				return result2;
			}
			
			int[] result3 = isInCheck2( color,WQ, ROOK_ATTACK, kingIdx, allPieces);
			if ( result3 != null) {
				return result3;
			}
			int[] result4 = isInCheck2( color,WQ, BISHOP_ATTACK, kingIdx, allPieces);
			if ( result4 != null) {
				return result4;
			}
			
			int[] result5=  knightCheck( color, kingIdx);
			if ( result5 != null) {
				return result5;
			}
			
			int[] result6 = pawnCheck( color, kingIdx);
			if ( result6 != null) {
				return result6;
			}
			
			return null;
		}
		private  int[] pawnCheck( int color, int kingIdx) {
			long pawns = bitboard[WP + (1-color)];
			long x = Movegen.pawnCheckArray[(1-color)][kingIdx];
			if  (( x  & pawns) != 0 ) {
				int idx = Long.numberOfTrailingZeros(pawns);
				while (idx < 64)
				{
					long justOnePawn = 1L<<idx;
					
					if ( (x & justOnePawn ) != 0 ) {
						int[] out = {idx};
						
						return out;
					}
					pawns = pawns & ( pawns - 1 );
					idx = Long.numberOfTrailingZeros(pawns);
				}
			}
			return null;
		}
		private  int[] knightCheck( int color, int kingIdx) {
			long x = Movegen.knightMoveArray[kingIdx];
			long knights = bitboard[WN + (1-color)];
			if ( (x & knights ) != 0 )  {
				int idx = Long.numberOfTrailingZeros(knights);
				while (idx < 64)
				{
					long justOneKnight = 1L << idx;
					if ( (x & justOneKnight ) != 0 ) {
						int[] out = {idx};
						return out;
					}
					knights = knights & ( knights - 1 );
					idx = Long.numberOfTrailingZeros(knights);
				}
			}
			return null;
		}
		private  int[] isInCheck2( int color, int piece, int type, int kingIdx, long allPieces) {
			// returns array of squares
			// if color is in check return keysquare array
			// first square is the attacking piece
			
			long enemyPiece  = bitboard[piece+ ( 1-color)] ;    // could be two rooks, for example
			//long enemyBishops = gs.bitboard[GameState.WQ+ ( 1-color)] ;
			long attacks1;
			
			if (type == ROOK_ATTACK) {
				attacks1 = RookMagic.getRookAttacks(kingIdx,allPieces) &  enemyPiece;
			}
			else {
				attacks1 = BishopMagic.getBishopAttacks(kingIdx,allPieces) &  enemyPiece;
			}
			int[] out = new int[8];
			int k=0;
			if (attacks1!= 0) 
			{
				int idx = Long.numberOfTrailingZeros(enemyPiece);
				while (idx < 64)
				{
					long oneEnemyPiece = 1L << idx;
					
					//System.out.println("attack idx " + idx);
					if ((oneEnemyPiece & attacks1) != 0 ) {
						
						out[k++]=idx;
						
						long inBetweenSquares = BitUtil.inBetweenWithoutLookup(idx, kingIdx);
						int idx2 = Long.numberOfTrailingZeros(inBetweenSquares);
						
						while (idx2 < 64){
								//System.out.println("rook in between idx " + idx);
							    out[k++]=idx2;
							   
								inBetweenSquares = inBetweenSquares & ( inBetweenSquares - 1 );
								idx2 = Long.numberOfTrailingZeros(inBetweenSquares);
						}
						break;
					}
					enemyPiece = enemyPiece & ( enemyPiece - 1 );
					idx = Long.numberOfTrailingZeros(enemyPiece);
				}
			}
			
			if (k > 0) {
				int[] out2 = new int[k];
				System.arraycopy(out, 0, out2, 0, k );
				return out2;
			}
			return null;
		}
		
		
		
		private static int getDistance(int sq1, int sq2) {
			   int file1, file2, rank1, rank2;
			   int rankDistance, fileDistance;
			   file1 = sq1  & 7;
			   file2 = sq2  & 7;
			   rank1 = sq1 >> 3;
			   rank2 = sq2 >> 3;
			   rankDistance = Math.abs (rank2 - rank1);
			   fileDistance = Math.abs (file2 - file1);
			   return Math.max (rankDistance, fileDistance);
			}
		
		public static void initDistanceTable() {
			for (int i=0; i < 64; i++) {
				for (int j=0; j < 64; j++) {
					distance[i][j] = getDistance(i,j);
				}
			}
		}
	/*	public  int tropismPenalty(int color) {    // the bigger the number, the closer the enemy's pieces are to my king
			int tropism=0;
			int danger=1;
			int idx = Long.numberOfTrailingZeros(bitboard[WK+color]);
			int[] enemyQueen={BQ,WQ};
			int[] enemyBishop={BB,WB};
			int[] enemyRook={BR,WR};
			
			long enemyPawns=bitboard[WP+(1-color)];
			
			long enemyAttackers= bitboard[WHITEPIECES+(1-color)] & (~enemyPawns) ;
			int idx2 = Long.numberOfTrailingZeros(enemyAttackers);
			while (idx2 < 64) {
				int dist=(8 - distance[idx][idx2] );
				int piece= board[63-idx2];
				
				if (piece==enemyQueen[color]) 
				{
					//System.out.println("Enemy Queen at index " + idx2);
					danger=4;
				}
				else 
					if ((piece==enemyBishop[color]) || (piece==enemyRook[color])) {
						//System.out.println("Enemy Queen at index " + idx2);
						danger=1;
					}
				else {
					danger=2;
				}
				tropism += dist*danger;
				enemyAttackers = enemyAttackers & ( enemyAttackers - 1 );
				idx2 = Long.numberOfTrailingZeros(enemyAttackers);
			}
			return -tropism;
		}*/
	
		
	
}
